package com.yupi.yupicturebackend.manager.auth;

import cn.dev33.satoken.stp.StpInterface;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.Header;
import cn.hutool.json.JSONUtil;
import com.yupi.yupicturebackend.enums.SpaceRoleEnum;
import com.yupi.yupicturebackend.enums.SpaceTypeEnum;
import com.yupi.yupicturebackend.exception.BusinessException;
import com.yupi.yupicturebackend.exception.ErrorCode;
import com.yupi.yupicturebackend.manager.auth.model.SpaceUserPermissionConstant;
import com.yupi.yupicturebackend.model.entity.Picture;
import com.yupi.yupicturebackend.model.entity.Space;
import com.yupi.yupicturebackend.model.entity.SpaceUser;
import com.yupi.yupicturebackend.model.entity.User;
import com.yupi.yupicturebackend.service.PictureService;
import com.yupi.yupicturebackend.service.SpaceService;
import com.yupi.yupicturebackend.service.SpaceUserService;
import com.yupi.yupicturebackend.service.UserService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;

import static com.yupi.yupicturebackend.constant.UserConstant.USER_LOGIN_STATE;

@Component
public class StpInterfaceImpl implements StpInterface {
    // 指的的配置文件的/api
    @Value("${server.servlet.context-path}")
    private String contextPath;
    @Resource
    private SpaceUserAuthManager spaceUserAuthManager;
    @Resource
    private SpaceUserService spaceUserService;
    @Resource
    private PictureService  pictureService;
    @Resource
    private UserService userService;
    @Resource
    private SpaceService spaceService;

    /**
     * 空间成员获取权限列表
     * @param loginId
     * @param loginType
     * @return
     */
    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        // 仅针对类型为space的进行权限校验
        if (!StpKit.SPACE_TYPE.equals(loginType)) {
            return new ArrayList<>();
        }
        // 管理员权限，表示校验通过
        List<String> ADMIN_PERMISSIONS = spaceUserAuthManager.getPermissionByRole(SpaceRoleEnum.ADMIN.getValue());
        // 获取上下文对象
        SpaceUserAuthContext authContext = getAuthContextByRequest();
        // 如果所有字段都为空，表示查询公共图库，可以通过
        if (ifAllFieldsNull(authContext)) {
            return ADMIN_PERMISSIONS;
        }
        // 获取userId
        User loginUser = (User) StpKit.SPACE.getSessionByLoginId(loginId).get(USER_LOGIN_STATE);
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR, "用户未登录");
        }
        Long userId = loginUser.getId();
        // 优先从上下文中获取spaceUser对象
        SpaceUser spaceUser = authContext.getSpaceUser();
        // 有空间用户  直接根据用户角色获取权限
        if (spaceUser != null) {
            return spaceUserAuthManager.getPermissionByRole(spaceUser.getSpaceRole());
        }
        // 没有空间用户信息 通过spaceUserId判断权限
        Long spaceUserId = authContext.getSpaceUserId();
        // 如果有spaceUserId  那么必然是团队空间，通过数据库查询spaceUser对象
        if (spaceUserId != null) {
            // 获取空间用户信息
            spaceUser = spaceUserService.getById(spaceUserId);
            // 用户不存在  直接报错
            if (spaceUser == null) {
                throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "未找到空间用户信息");
            }
            // 用户存在 根据当前的用户id和spaceId查库 确保是当前登录用户的空间
            // 取出当前空间用户对应的spaceUser
            SpaceUser loginSpaceUser = spaceUserService.lambdaQuery()
                    .eq(SpaceUser::getSpaceId, spaceUser.getSpaceId())
                    .eq(SpaceUser::getUserId, userId)
                    .one();
            if (loginSpaceUser == null) {
                return new ArrayList<>();
            }
            // 这里会导致管理员在私有空间没有权限，可以再查一次库处理
            return spaceUserAuthManager.getPermissionByRole(loginSpaceUser.getSpaceRole());
        }
        // 如果没有spaceUserId 尝试通过spaceId或pictureId获取space对象 并处理
        Long spaceId = authContext.getSpaceId();
        if (spaceId == null) {
            // 如果没有spaceId  通过pictureId获取picture对象和space对象
            Long pictureId = authContext.getPictureId();
            // 全是空 表示公共图库
            if (pictureId == null) {
                return ADMIN_PERMISSIONS;
            }
            Picture picture = pictureService.lambdaQuery()
                    .eq(Picture::getId, pictureId)
                    .select(Picture::getId, Picture::getSpaceId, Picture::getUserId)
                    .one();
            if (picture == null) {
                throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "未找到图片信息");
            }
            spaceId = picture.getSpaceId();
            // 公共图库中的图片，仅本人上传本人或管理员可操作
            if (spaceId == null) {
                if (picture.getUserId().equals(userId) || userService.isAdmin(loginUser)) {
                    return ADMIN_PERMISSIONS;
                } else {
                    // 不是自己图片 仅查看
                    return Collections.singletonList(SpaceUserPermissionConstant.PICTURE_VIEW);
                }
            }
        }
        // spaceId存在，获取space对象
        Space space = spaceService.getById(spaceId);
        if (space == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "未找到空间信息");
        }
        // 根据space类型判断权限
        if (Objects.equals(space.getSpaceType(), SpaceTypeEnum.PRIVATE.getValue())) {
            // 私有空间，仅本人或管理员有权限
            if (space.getUserId().equals(userId) || userService.isAdmin(loginUser)) {
                return ADMIN_PERMISSIONS;
            } else {
                return new ArrayList<>();
            }
        } else {
            // 团队空间 获取spaceUser对象 并处理
            spaceUser = spaceUserService.lambdaQuery()
                    .eq(SpaceUser::getSpaceId, spaceId)
                    .eq(SpaceUser::getUserId, userId)
                    .one();
            if (spaceUser == null) {
                return new ArrayList<>();
            }
            return spaceUserAuthManager.getPermissionByRole(spaceUser.getSpaceRole());
        }
    }

    private boolean ifAllFieldsNull(Object object) {
        if (object == null) {
            return true;
        }
        // 通过反射获取所有字段，并判断是不是空
        // 获取传参object的所有字段
        return Arrays.stream(ReflectUtil.getFields(object.getClass()))
                // 获取传参object所有字段的值
                .map(filed -> ReflectUtil.getFieldValue(object, filed))
                // 判断是不是满足所有的值都为空
                .allMatch(ObjectUtil::isEmpty);
    }

    @Override
    public List<String> getRoleList(Object o, String s) {
        return null;
    }

    private SpaceUserAuthContext getAuthContextByRequest() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        String contextType = request.getHeader(Header.CONTENT_TYPE.getValue());
        SpaceUserAuthContext authRequest;
        // 兼容get和post操作，post请求有请求体，所以header中包含有contentType
        if (ContentType.JSON.getValue().equals(contextType)) {
            // 处理post请求，获取body，转换成对象
            String body = ServletUtil.getBody(request);
            authRequest = JSONUtil.toBean(body, SpaceUserAuthContext.class);
        } else {
            // 处理get请求，get请求没有body  请求参数在url中，所以需要解析url
            Map<String, String> paramMap = ServletUtil.getParamMap(request);
            authRequest = BeanUtil.toBean(paramMap, SpaceUserAuthContext.class);
        }
        // 根据请求路径区分id字段的含义
        Long id = authRequest.getId();
        if (ObjectUtil.isNotNull(id)) {
            // 举例：URI路径为/api/picture/123
            // 当前行代码作用为获取完整URI，即：/api/picture/123
            String requestURI = request.getRequestURI();
            // 当前行代码的作用为去掉 contextPath/，我们配置的contextPath为/api，所以得到的partURI为picture/123
            String partURI = requestURI.replace(contextPath + "/", "");
            // 当前行代码为获取/前的内容 也就是从picture/123中获取picture
            String moduleName = StrUtil.subBefore(partURI, "/", false);
            switch (moduleName) {
                case "picture":
                    authRequest.setPictureId(id);
                    break;
                case "space":
                    authRequest.setSpaceId(id);
                    break;
                case "spaceUser":
                    authRequest.setSpaceUserId(id);
                    break;
                default:
            }
        }
        return authRequest;
    }
}
